package com.ff.javamonkey.ui;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.ff.javamonkey.R;
import com.ff.javamonkey.file.LocatorVerificator;
import com.ff.javamonkey.socket.ClientConnect;
import com.ff.javamonkey.socket.SFTPClientConnect;
import com.ff.javamonkey.ui.notification.NotificationView;
import com.ff.javamonkey.util.ConnectionSettings;
import com.ff.javamonkey.util.IOUtils;
import com.ff.javamonkey.util.StringUtils;

/**
 * Main entry to the application. Still needs to be refined: - onResume, onStop,
 * and onDestroy. What to do with it, and how to do it. - AsyncTask canceled. -
 * on ClientConnectionGetFileTask, if there's an IOException during getFile,
 * what to do?
 * 
 * @author rhama_arya
 */
public class MainActivity extends Activity {
	// Debugging
	private static final String TAG = "MainActivity";

	// ClientSocket
	private ClientConnect client;

	// UI components
	private TextView txtMainActivityStatus;
	private ProgressBar progressConnection;

	// What we're doing?
	private CurrentStatus currentStatus = CurrentStatus.NONE; // Dirty check.
	
	private enum CurrentStatus {
		NONE, START_CONNECTING, DONE_CONNECTING
	}

	// What files are we going to have.
	private String[] allACRLFilesOnServer;

	public Handler handler = new Handler();

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		txtMainActivityStatus = (TextView) findViewById(R.id.txtConnectStatus);
		progressConnection = (ProgressBar) findViewById(R.id.progBarMainActivity);
		progressConnection.setVisibility(View.INVISIBLE);
	}

	@Override
	public void onStop() {
		super.onStop();
	}

	@Override
	public void onResume() {
		super.onResume();
		if (currentStatus == CurrentStatus.NONE) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage(
					getString(R.string.connect_confirmation) + " "
							+ ConnectionSettings.FDCU_PARENT_HOST + " ?")
					.setCancelable(false)
					.setTitle(getString(R.string.connect_title))
					.setPositiveButton("Yes",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									new ClientConnectionTask().execute();
								}
							})
					.setNegativeButton("No",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									MainActivity.this.finish();
								}
							})
					.setNeutralButton("Settings",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									Intent intent = new Intent(
											MainActivity.this,
											PreferencesActivity.class);
									startActivity(intent);
								}
							});
			AlertDialog alert = builder.create();
			alert.show();
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		if (client != null && client.isConnected()) {
			try {
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Called after ClientConnectionGetFileTask is completed.
	 * 
	 * @param result
	 *            it holds the status of the file read. Whether we read FDCU or
	 *            ACRL
	 * @param results
	 *            the byte that holds the resulting file read from the server.
	 */
	private void onTaskGetFileComplete(Integer result, String[] results) {
		progressConnection.setVisibility(View.INVISIBLE);
		// We failed to import
		if (results == null) {
			txtMainActivityStatus.setText("Failed to obtain file");
			return;
		} else {
			startACRLTransfer(results);
		}
	}

	private void goDownloadFile(String[] result) {
		Log.i(TAG, result.length + " result length");
		for (String str : result) {
			Log.i(TAG, str + " in goDownloadFile");
		}
		new ClientConnectionGetFileTask().execute(result);
	}

	/**
	 * This method called to start ACRLTransferActivity. Pre-condition: The ACRL
	 * file read, must be stored inside the agreed directory specified in
	 * onTaskGetFileComplete method.
	 */
	private void startACRLTransfer(String[] results) {
		// Intent intent = new Intent(this, ACRLTransferActivity.class);
		// intent.putExtra(ACRLTransferActivity.FILE_PATH, results);
		Intent intent = new Intent(this, ACRLListActivity.class);
		// intent.putExtra(ACRLListActivity.FILE_PATH, results);
		intent.putExtra(ACRLListActivity.FILE_PATH, allACRLFilesOnServer);
		startActivity(intent);
	}

	private boolean haveConnection() {
		ConnectivityManager connMgr = (ConnectivityManager) this
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		final NetworkInfo wifi = connMgr
				.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
		final NetworkInfo mobile = connMgr
				.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
		if (wifi.isAvailable() || mobile.isAvailable())
			return true;
		return false;
	}

	/**
	 * This AsyncTask tries to establishes connection with the server (using
	 * SFTP). preExecute: - Check if a client is not null and is already
	 * connected. - If not, create a connection to the host (defined by
	 * FDCU_PARENT_HOST constant variable). doInBackground: - Check periodically
	 * for any update on connection status (every 100 ms, by a call to
	 * Thread.seep()). - Connect, and return CLIENT_CONNECTION_SUCCESSFUL if
	 * accepted. - (Not yet implemented) Sending an informative message if a
	 * connection is unsuccessful.
	 * 
	 * @author rhama_arya
	 */
	private class ClientConnectionTask extends AsyncTask<Void, String, Integer> {
		private int status = 0;
		NotificationManager mNotificationManager;

		protected void onPreExecute() {
			if (client != null && client.isConnected())
				;
			else
				client = new SFTPClientConnect(
						ConnectionSettings.FDCU_PARENT_HOST);
			// Preparing UI for connecting info and status.
			progressConnection.setVisibility(View.VISIBLE);
			mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
			currentStatus = CurrentStatus.START_CONNECTING; // This is dangerous. We need to
												// regularily check this
												// variable.
		}

		@Override
		protected Integer doInBackground(Void... params) {
			// You don't have any feasible connection. Abort.
			// if (!haveConnection()) {
			// return -2;
			// }
			new Thread(new Runnable() {
				public void run() {
					try {
						client.connect(ConnectionSettings.FDCU_PARENT_USER,
								ConnectionSettings.FDCU_PARENT_PASS);
						client.setConnectionStatus(ClientConnect.CLIENT_CONNECTION_SUCCESSFUL);
						status = 1; // if we managed to establish a connection.
					} catch (IOException e) {
						final String eMessage = e.getLocalizedMessage();
						client.setConnectionStatus(ClientConnect.CLIENT_CONNECTION_REFUSED);
						status = -1; // we can't establish a connection
						publishProgress(eMessage);
						e.printStackTrace();
					}
				}
			}).start();
			long begins = System.currentTimeMillis();
			while (status == 0) {
				publishProgress(
						client.getMessage() == null ? "Start Connecting"
								: client.getMessage(),
						StringUtils.getTimeDelta(begins));
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return status;
		}

		@Override
		protected void onProgressUpdate(String... progress) {
			if (progress[1] != null) {
				mNotificationManager.notify(1, NotificationView
						.generalNotification(getApplicationContext(),
								progress[0], progress[1]));
			}
			txtMainActivityStatus.setText(progress[0]);
		}

		@Override
		protected void onPostExecute(Integer result) {
			// Log.i(TAG, "Return result " + result);
			progressConnection.setVisibility(View.GONE);
			if (!client.isConnected()) {
				txtMainActivityStatus.setText("Connection failed");
				// notificationMainView.setNotification(1, "Connection failed",
				// "Sorry");
				mNotificationManager.notify(1, NotificationView
						.generalNotification(getApplicationContext(),
								"Connection failed", "Sorry"));
			} else {
				txtMainActivityStatus.setText("Connection successful");
				// notificationMainView.setNotification(1,
				// "Connection successful", "Proceed");
				mNotificationManager.notify(1, NotificationView
						.generalNotification(getApplicationContext(),
								"Connection successful", "Proceed"));
			}
			currentStatus = CurrentStatus.DONE_CONNECTING;
			onTaskComplete();
			System.gc();
		}
	}

	/**
	 * Called after ClientConnectionTask is completed. This method very
	 * dependent on the server (FDCS) having both the .db and .sum file and
	 * named appropriately. It assumes that for every .db files out there, there
	 * is a corresponding and identical .sum file as well.
	 */
	private void onTaskComplete() {
		if (client.isConnected()) {
			// Do whatever necessary when we've established a connection.
			// Get the ARCL file from the server.
			// 1. Query its file size,
			// 2. GET IT.
			progressConnection.setVisibility(View.VISIBLE);
			progressConnection.setIndeterminate(true);
			txtMainActivityStatus
					.setText("Attempting to obtain the ARCL File.");
			try {
				String[] files = client.getFileNames(
						ConnectionSettings.FDCU_ACRL_DBARCHIVE, "*.sum");
				allACRLFilesOnServer = client.getFileNames(
						ConnectionSettings.FDCU_ACRL_DBARCHIVE, "a*.db");
				new ClientConnectionVerifyFiles().execute(files);

			} catch (Exception e) {
				txtMainActivityStatus.setText("Obtain file failed");
				e.printStackTrace();
			}
		} else {
			// Debugging purpose
			Log.e(TAG, "Not Connected");
		}
	}

	/*
	 * Trying to get a file from server.
	 */
	private class ClientConnectionGetFileTask extends
			AsyncTask<String, Integer, Integer> {
		int fileSize;
		int totalSize = 0;
		int status = 0;
		int i = 0; // runner?
		String innerFile;
		String[] filesObtained;
		NotificationManager mNotificationManager;

		protected void onPreExecute() {
			if (client != null && client.isConnected())
				;
			else
				client = new SFTPClientConnect(
						ConnectionSettings.FDCU_PARENT_HOST);
			progressConnection.setIndeterminate(false);
			progressConnection.setProgress(0);
			mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		}

		@Override
		protected Integer doInBackground(String... arg0) {
			final int totalFiles = arg0.length;
			// Multiple files.
			if (!client.isConnected())
				try {
					client.connect(ConnectionSettings.FDCU_PARENT_USER,
							ConnectionSettings.FDCU_PARENT_PASS);
				} catch (IOException e) {
					e.printStackTrace();
					return -1;
				}
			new Thread(new Runnable() {
				public void run() {
					while (i != totalFiles) {
						if (client.getReadStatus() < fileSize) {
							publishProgress(i, client.getReadStatus(), fileSize);
						}
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}).start();
			// Hack.
			filesObtained = new String[arg0.length];
			int j = 0;
			for (String s : arg0) {
				try {
					String file = new StringBuilder()
							.append(ConnectionSettings.FDCU_ACRL_DBARCHIVE)
							.append("/").append(s).toString();
					fileSize = client.getFileSize(file);
					totalSize += fileSize;
					File dir = new File(
							Environment.getExternalStorageDirectory(), "ff");
					if (!dir.exists())
						if (!dir.mkdir())
							return -1; // Failed to make a directory.
					File fileOut = new File(
							Environment.getExternalStorageDirectory(), "ff/"
									+ s);
					Log.i(TAG, "Output file " + fileOut.toString());
					client.getFile(file, fileOut.toString());
					filesObtained[j++] = s;
					publishProgress(i++, 100, 100);
					IOUtils.copied = 0L; // hard reset copied static variable.
				} catch (IOException e) {
					publishProgress(i++, 0, 0);
					e.printStackTrace();
				}
			}
			return 1;
		}

		@Override
		protected void onProgressUpdate(Integer... progress) {
			if (progress[2] == 0)
				return; // Make sure we don't have division by zero;
			int prog = (progress[1] * 100 / progress[2]);
			progressConnection.setProgress(prog);
			mNotificationManager
					.notify(2, NotificationView.notificationWithProgressBar(
							getApplicationContext(), "Downloading...",
							filesObtained[progress[0]], prog));
		}

		@Override
		protected void onPostExecute(Integer result) {
			if (result != -1) {
				progressConnection.setProgress(100);
				mNotificationManager.notify(2,
						NotificationView.generalNotification(
								getApplicationContext(), filesObtained.length
										+ " ACRL Files Downloaded", "Done"));
				txtMainActivityStatus.setText("Success");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				onTaskGetFileComplete(result, filesObtained);
			} else {
				onTaskGetFileComplete(result, null);
			}
		}
	}

	// Trying to verify files from server.
	private class ClientConnectionVerifyFiles extends
			AsyncTask<String, Integer, String[]> {
		// Debugging
		private final String TAG = "ClientConnectionVerfiyFiles";

		private LocatorVerificator locatorVerificator;
		byte[] results = new byte[0];
		int i = 0; // runner?
		int fileSize;

		@Override
		protected void onPreExecute() {
			// Until we discovers a slightly more robust solution, we open a
			// client here.
			if (client != null && client.isConnected())
				;
			else
				client = new SFTPClientConnect(
						ConnectionSettings.FDCU_PARENT_HOST);
			locatorVerificator = new LocatorVerificator();
		}

		@Override
		protected String[] doInBackground(String... str) {
			final int totalFiles = str.length;
			// currentWork = integers[0];
			if (!client.isConnected())
				try {
					client.connect(ConnectionSettings.FDCU_PARENT_USER,
							ConnectionSettings.FDCU_PARENT_PASS);
				} catch (IOException e) {
					e.printStackTrace();
					return null;
				}
			new Thread(new Runnable() {
				public void run() {
					while (i != totalFiles) {
						if (client.getReadStatus() < fileSize) {
							// We doesn't need to publish. We're reading a 40
							// bytes file anyway.
							// publishProgress(i, client.getReadStatus(),
							// fileSize);
						}
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}).start();
			// Browse for files .sum
			ArrayList<String> filesToDownload = new ArrayList<String>();
			for (String s : str) {
				try {
					String file = new StringBuilder()
							.append(ConnectionSettings.FDCU_ACRL_DBARCHIVE)
							.append("/").append(s).toString();
					String localAcrlFile = s.split("[.]")[0];
					String localFile = new StringBuilder()
							.append(ConnectionSettings.LOCAL_ACRL_DBARCHIVE)
							.append("/").append(localAcrlFile).append(".db")
							.toString();
					fileSize = client.getFileSize(file);
					results = client.getFile(file); // we grab the content of
													// the file. The content
					String hashed = new String(results);
					if (locatorVerificator.verify(new File(localFile), hashed)) { // If
																					// true,
																					// verified,
																					// no
																					// need
																					// to
																					// download
						continue;
					} else {
						filesToDownload.add(localAcrlFile + ".db");
					}
					publishProgress(i++, 100, 100);
					IOUtils.copied = 0L; // hard reset copied static variable.
				} catch (IOException e) {
					publishProgress(i++, 0, 0);
					e.printStackTrace();
				}
			}
			return filesToDownload.toArray(new String[filesToDownload.size()]);
		}

		@Override
		protected void onProgressUpdate(Integer... progress) {

		}

		@Override
		protected void onPostExecute(String... result) {
			goDownloadFile(result);
		}
	}
}